package io.kiki.task.client.common.rpc.supports.scan;

import io.kiki.task.client.common.annotation.EndPoint;
import io.kiki.task.client.common.annotation.Path;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@Slf4j
@Component
public class SnailEndPointScanner implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public List<EndPointInfo> doScan() {
        return scanEndPoint();
    }

    private List<EndPointInfo> scanEndPoint() {

        List<EndPointInfo> endPointInfoList = new ArrayList<>();
        String[] beanDefinitionNames = applicationContext.getBeanNamesForType(Object.class, false, true);
        for (String beanDefinitionName : beanDefinitionNames) {
            Object bean = applicationContext.getBean(beanDefinitionName);
            Class executorNotProxy = AopProxyUtils.ultimateTargetClass(bean);
            String executorClassName = executorNotProxy.getName();

            // 扫描类的注解
            EndPoint endPoint = bean.getClass().getAnnotation(EndPoint.class);
            if (Objects.nonNull(endPoint)) {
                Map<Method, Path> annotatedMethods = null;
                try {
                    annotatedMethods = MethodIntrospector.selectMethods(bean.getClass(),
                            (MethodIntrospector.MetadataLookup<Path>) method -> AnnotatedElementUtils
                                    .findMergedAnnotation(method, Path.class));
                } catch (Throwable ex) {
                    log.error("{} Mapping加载异常：{}", beanDefinitionName, ex);
                }

                for (Map.Entry<Method, Path> entry : annotatedMethods.entrySet()) {
                    Method method = entry.getKey();
                    Path path = entry.getValue();
                    endPointInfoList.add(EndPointInfo.builder()
                            .executorName(executorClassName)
                            .method(method)
                            .executor(bean)
                            .path(path.value())
                            .requestMethod(path.method())
                            .build());
                }
            }

        }

        return endPointInfoList;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
